using System;
using System.Linq;
using System.Collections.Generic;
using System.Xml.Serialization;

namespace Ataric.Themester.Parser {
	/// <summary></summary>
    /// 
    [Serializable]
	public class RuleSet : IDeclarationContainer {
		private List<Selector> selectors = new List<Selector>();
		private List<Declaration> declarations = new List<Declaration>();

		/// <summary></summary>
		[XmlArrayItem("Selector", typeof(Selector))]
		[XmlArray("Selectors")]
		public List<Selector> Selectors {
			get { return selectors; }
			set { selectors = value; }
		}

		/// <summary></summary>
		[XmlArrayItem("Declaration", typeof(Declaration))]
		[XmlArray("Declarations")]
		public List<Declaration> Declarations {
			get { return declarations; }
			set { declarations = value; }
		}

		/// <summary></summary>
		/// <returns></returns>
		public override string ToString() {
			return ToString(0,true);
		}

        public string ToString(int nesting)
        {
            return ToString(0, false);
        }
		/// <summary></summary>
		/// <param name="nesting"></param>
		/// <returns></returns>
		public string ToString(int nesting, bool addTab) {
			/*
ruleset<out RuleSet rset> =		(. rset = new RuleSet();
									Selector sel = null;
									Declaration dec = null;
								.)
	selector<out sel>			(. rset.Selectors.Add(sel); .)
	{ ',' selector<out sel>		(. rset.Selectors.Add(sel); .)
	}
    '{' declaration<out dec>	(. rset.Declarations.Add(dec); .)
	{ ';' declaration<out dec>	(. rset.Declarations.Add(dec); .)
	} [ ';' ] '}'
.
			*/
			string start = "";
			for (int i = 0; i < nesting; i++) {
				start += "\t";
			}

			System.Text.StringBuilder txt = new System.Text.StringBuilder();
			bool first = true;
			foreach (Selector sel in selectors) {
				if (first) { first = false; txt.Append(start); } else { txt.Append(", "); }
				txt.Append(sel.ToString());
			}
			txt.Append(" {\r\n");
			txt.Append(start);

			foreach (Declaration dec in declarations) {
				txt.AppendFormat("{2}{0};\r\n{1}", dec.ToString(), start, addTab?"\t":"");
			}

			txt.Append("}");
			return txt.ToString();
		}

        /// <summary>
        /// Use this to split all complete rule to normal rule.
        /// </summary>
        public void SplitOut()
        {
            List<String> combinationNames = new List<string>()  {"border-left","border-right","border-top","border-bottom","border","font", 
                                                                 "background", "list-style","margin","padding","outline", "overflow"};

            
            List<String> complexDeclarationNames = new List<string>() 
            {"border-left","border-right","border-top","border-bottom","border-collapse","border-spacing",
            "caption-side","counter-increment","counter-reset","cue-after","cue-before","empty-cells",
            "letter-spacing","line-height","list-style","page-break-after","page-break-before","page-break-inside",
            "max-height","min-height","min-width","max-width","table-layout","text-align","text-decoration","text-indent","text-transform",
            "vertical-align","white-space","word-spacing","z-index", "overflow-x","overflow-y"
            };

            List<Declaration> combinationRules = this.Declarations.Where(p => combinationNames.Any(s => p.Name == s))
                                                          .OrderBy(p => p.Name)
                                                          .ToList();
            List<Declaration> singleRules = new List<Declaration>();
            foreach (var declaration in this.Declarations)
            {
                switch (declaration.Name)
                {
                    case "border":
                    case "border-left":
                    case "border-right":
                    case "border-top":
                    case "border-bottom":
                        SplitBorderNote(declaration, singleRules);
                        break;

                    case "background":

                        SplitBackgroundNote(declaration, singleRules);
                        break;
                    case "font":

                        SplitFontNote(declaration, singleRules);
                        break;
                    case "list-style":

                        SplitListNote(declaration, singleRules);
                        break;
                    case "margin":

                        SplitMarginNote(declaration, singleRules);
                        break;
                    case "padding":

                        SplitPaddingNote(declaration, singleRules);
                        break;
                    case "outline":

                        SplitOutlineNote(declaration, singleRules);
                        break;
                    default:
                        singleRules.Add(declaration);
                        break;
                }
            }
            this.Declarations = SimpleMerge(singleRules);

        }

        private List<Declaration> SimpleMerge(List<Declaration> singleRules)
        {
            for (int i = 0; i < singleRules.Count; i++)
            {
                var item = singleRules.Where(p => p != singleRules[i] && p.Name == singleRules[i].Name).ToList();

                foreach (var rule in item) {
                    singleRules[i].Expression.Terms.AddRange(rule.Expression.Terms);
                    singleRules.Remove(rule);
                }

            }
            return singleRules;
        }

        private void SplitOutlineNote(Declaration declaration, List<Declaration> singleRules)
        {
            foreach (var term in declaration.Expression.Terms)
            {

                Declaration d = new Declaration()
                {
                    Expression = new Expression()
                    {
                        Terms = new List<Term>() {
                                term
                            }
                    }
                };

                if (term.IsColor)
                {
                    d.Name = declaration.Name + "-color";
                }
                else
                    if (term.Value.ToLower() == "none"
                        || term.Value.ToLower() == "dotted"
                        || term.Value.ToLower() == "dashed"
                        || term.Value.ToLower() == "solid"
                        || term.Value.ToLower() == "double"
                        || term.Value.ToLower() == "groove"
                        || term.Value.ToLower() == "ridge"
                        || term.Value.ToLower() == "inset"
                        || term.Value.ToLower() == "outset"

                    )
                    {
                        d.Name = declaration.Name + "-style";
                    }
                    else
                        if (term.Value.ToLower() == "thin"
                                 || term.Value.ToLower() == "medium"
                                || term.Value.ToLower() == "thick"
                            || term.Value.ToLower() == "length"
                                )
                        {
                            d.Name = declaration.Name + "-width";
                        }
                singleRules.Add(d);
            }
        }

        private void SplitPaddingNote(Declaration declaration, List<Declaration> singleRules)
        {
            List<string> srr = new List<string>() { "top", "right", "bottom", "left" };
            for (int i = 0; i < declaration.Expression.Terms.Count && i<4; i++)
            {
                Declaration d = new Declaration()
                {
                    Expression = new Expression()
                    {
                        Terms = new List<Term>() {
                                declaration.Expression.Terms[i]
                            }
                    },
                    Name = "padding-" + srr[i]
                };


                singleRules.Add(d);
            }
        }

        private void SplitMarginNote(Declaration declaration, List<Declaration> singleRules)
        {
            List<string> srr = new List<string>() { "top", "right", "bottom", "left" };
            for (int i=0;i< declaration.Expression.Terms.Count && i<4; i++)
            {
                Declaration d = new Declaration()
                {
                    Expression = new Expression()
                    {
                        Terms = new List<Term>() {
                                declaration.Expression.Terms[i]
                            }
                    },
                    Name = "margin-"+ srr[i]
                };

                
                singleRules.Add(d);
            }
        }

        private void SplitListNote(Declaration declaration, List<Declaration> singleRules)
        {
            foreach (var term in declaration.Expression.Terms)
            {

                Declaration d = new Declaration()
                {
                    Expression = new Expression()
                    {
                        Terms = new List<Term>() {
                                term
                            }
                    }
                };

                if (term.Type == TermType.Url)
                {
                    d.Name = declaration.Name + "-image";
                    //List<Term> list = declaration.Expression.Terms.Where(p => !p.IsColor && p.Type != TermType.Url).ToList();
                    //d.Expression.Terms.AddRange(list);
                }
                else
                    if (term.Value.ToLower() == "inside"
                        || term.Value.ToLower() == "outside"
                    
                    )
                {
                    d.Name = declaration.Name + "-position";
                }
                else
                        if (term.Value.ToLower() == "circle"
                                 || term.Value.ToLower() == "square"
                                || term.Value.ToLower() == "upper-roman"
                            || term.Value.ToLower() == "lower-alpha"
                            || term.Value.ToLower() == "armenian"
                            || term.Value.ToLower() == "cjk-ideographic"
                            || term.Value.ToLower() == "decimal"
                            || term.Value.ToLower() == "decimal-leading-zero"
                            || term.Value.ToLower() == "disc"
                            || term.Value.ToLower() == "georgian"
                            || term.Value.ToLower() == "hebrew"
                            || term.Value.ToLower() == "hiragana"
                            || term.Value.ToLower() == "hiragana-iroha"
                            || term.Value.ToLower() == "katakana"
                            || term.Value.ToLower() == "katakana-iroha"
                            || term.Value.ToLower() == "lower-greek"
                            || term.Value.ToLower() == "lower-latin"
                            || term.Value.ToLower() == "lower-roman"
                            || term.Value.ToLower() == "square"
                            || term.Value.ToLower() == "none"
                            || term.Value.ToLower() == "upper-latin"
                            || term.Value.ToLower() == "upper-roman"
                            || term.Value.ToLower() == "upper-latin"

                                )
                            {
                                d.Name = declaration.Name + "-style";
                            }
                singleRules.Add(d);
            }
        }

        private void SplitFontNote(Declaration declaration, List<Declaration> singleRules)
        {
            foreach (var term in declaration.Expression.Terms)
            {

                Declaration d = new Declaration()
                {
                    Expression = new Expression()
                    {
                        Terms = new List<Term>() {
                                term
                            }
                    }
                };

                if (term.Type == TermType.Number)
                {
                    d.Name = declaration.Name + "-size";
                    //List<Term> list = declaration.Expression.Terms.Where(p => !p.IsColor && p.Type != TermType.Url).ToList();
                    //d.Expression.Terms.AddRange(list);
                }
                else
                    if (term.IsColor)
                    {
                        d.Name = declaration.Name + "-color";
                    }
                    else
                        if (term.Value.ToLower() == "bold"
                             || term.Value.ToLower() == "bolder"
                            || term.Value.ToLower() == "lighter"
                            || term.Value.ToLower() == "normal"
                            )
                        {
                            d.Name = declaration.Name + "-weight";
                        }
                        else
                            if (term.Value.ToLower() == "normal"
                                 || term.Value.ToLower() == "italic"
                                || term.Value.ToLower() == "oblique"
                                
                                )
                            {
                                d.Name = declaration.Name + "-style";
                            }
                            else
                                if (term.Value.ToLower() == "small-caps")
                                {
                                    d.Name = declaration.Name + "-variant";
                                }
                        else
                        {
                            d.Name = declaration.Name + "-family";
                        }

                singleRules.Add(d);

            }
        }

        private void SplitBackgroundNote(Declaration declaration, List<Declaration> singleRules)
        {
            foreach (var term in declaration.Expression.Terms)
            {

                Declaration d = new Declaration()
                {
                    Expression = new Expression()
                    {
                        Terms = new List<Term>() {
                                term
                            }
                    }
                };

                if (term.Type ==  TermType.Url)
                {
                    d.Name = declaration.Name + "-image";
                    List<Term> list = declaration.Expression.Terms.Where(p => !p.IsColor && p.Type != TermType.Url).ToList();
                    d.Expression.Terms.AddRange(list);
                }
                else
                    if (term.IsColor)
                    {
                        d.Name = declaration.Name + "-color";
                    }
                    else
                        if (term.Value.Contains("repeat"))
                        {
                            d.Name = declaration.Name + "-repeat";
                        }
                        else
                        {
                            d.Name = declaration.Name + "-possition";
                        }

                singleRules.Add(d);

            }
        }

        private void SplitBorderNote(Declaration declaration, List<Declaration> singleRules)
        {
            foreach(var term in declaration.Expression.Terms){

                Declaration d = new Declaration()
                {
                    Expression = new Expression()
                    {
                        Terms = new List<Term>() {
                                term
                            }
                    }
                };

                if (term.UnitString == "PX")
                {
                    d.Name = declaration.Name + "-width";
                }
                else
                if (term.IsColor)
                {
                    d.Name = declaration.Name + "-color";
                }
                if (term.Type== TermType.String)
                {
                    d.Name = declaration.Name + "-style";
                }
                singleRules.Add(d);
            }
        }

        public void Normalize()
        {
            List<String> complexDeclarationNames = new List<string>() 
            {"border-collapse","border-spacing",
            "caption-side","counter-increment","counter-reset","cue-after","cue-before","empty-cells",
            "letter-spacing","line-height","page-break-after","page-break-before","page-break-inside",
            "max-height","min-height","min-width","max-width","table-layout","text-align","text-decoration","text-indent","text-transform",
            "vertical-align","white-space","word-spacing","z-index","word-wrap","overflow-x","overflow-y"
            };

            List<Declaration> normalizes = this.Declarations.Where(p=>!p.Name.Contains('-')
                                                                 || complexDeclarationNames.Any(s=>p.Name.StartsWith(s)))
                                                          .OrderBy(p=>p.Name)
                                                          .ToList();


            var childs = this.Declarations.Where(p => p.Name.Contains("-")
                                                        && !complexDeclarationNames.Any(s => s==p.Name)
                                                  )
                                             .ToList();

            foreach (var item in childs)
            {
                if (!normalizes.Any(p => item.Name.StartsWith(p.Name + "-")))
                {
                    string parent = item.Name.Substring(0, item.Name.LastIndexOf('-'));
                    normalizes.Add(new Declaration() { Name = parent, Expression = new Expression() { Terms = new List<Term>() } });
                }
            }

            foreach(var declaration in normalizes){
                var child = childs.Where(p => p.Name != declaration.Name 
                                                        && p.Name.StartsWith(declaration.Name)
                                                        && !complexDeclarationNames.Any(s=>p.Name.StartsWith(s))
                                                    )
                                             .ToList();
                switch (declaration.Name)
                {
                    case "border":
                    case "border-left":
                    case "border-right":
                    case "border-top":
                    case "border-bottom":
                        PopulateBorderNode(declaration, child);
                        break;

                    case "background":
                  
                        PopulateBackgroundNode(declaration, child);
                        break;
                    case "font":

                        PopulateFontNode(declaration, child);
                        break;
                    case "list-style":

                        PopulateListNode(declaration, child);
                        break;
                    case "margin":

                        PopulateListNode(declaration, child);
                        break;
                    case "padding":

                        PopulatePaddingNode(declaration, child);
                        break;
                    case "outline":

                        PopulateOutlineNode(declaration, child);
                        break;
                    default:
                        break;
                }

                                 
            }
            this.Declarations = normalizes;
        }
        //TODO : All of these functions below need to be implement to do correct normalization css.

        //TODO : Implement details
        private void PopulateOutlineNode(Declaration declaration, List<Declaration> childs)
        {
            foreach (var term in childs)
            {
                declaration.Expression.Terms.AddRange(term.Expression.Terms);
            }
        }


        //TODO : Implement details
        private void PopulatePaddingNode(Declaration declaration, List<Declaration> childs)
        {
            foreach (var term in childs)
            {
                declaration.Expression.Terms.AddRange(term.Expression.Terms);
            }
        }

        //TODO : Implement details
        private void PopulateMarginNode(Declaration declaration, List<Declaration> childs)
        {
            foreach (var term in childs)
            {
                declaration.Expression.Terms.AddRange(term.Expression.Terms);
            }
        }

        //TODO : Implement details
        private void PopulateListNode(Declaration declaration, List<Declaration> childs)
        {
            foreach (var term in childs)
            {
                declaration.Expression.Terms.AddRange(term.Expression.Terms);
            }
        }

        //TODO : Implement details
        private void PopulateFontNode(Declaration declaration, List<Declaration> childs)
        {
            foreach (var term in childs)
            {
                declaration.Expression.Terms.AddRange(term.Expression.Terms);
            }
        }
        //TODO : Implement details
        private void PopulateBackgroundNode(Declaration declaration, List<Declaration> childs)
        {
            foreach (var term in childs)
            {
                declaration.Expression.Terms.AddRange(term.Expression.Terms);
            }
        }
        //TODO : Implement details
        private void PopulateBorderNode(Declaration declaration, List<Declaration> childs)
        {

            foreach (var term in childs)
            {
                declaration.Expression.Terms.AddRange(term.Expression.Terms);
            }
        }
	}
} 